<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta charset="utf-8">
<title>Selector Test Suite</title>

<style type="text/css">
.yui-skin-sam .yui-console-entry-pass .yui-console-entry-cat {
    background: #070;
    color: #fff;
}
.yui-skin-sam .yui-console-entry-fail .yui-console-entry-cat {
    background: #700;
    color: #fff;
}
</style>
<script type="text/javascript" src="../../../../build/yui/yui.js"></script>

<script>
document.createElement('foo'); // enable custom element for IE native querySelector

onload = function() {

    var SELECTOR_MODULE = 'selector-css3';
    YUI({
        filter: (window.location.search.match(/[?&]filter=([^&]+)/) || [])[1] || 'raw'
    }).use('node', SELECTOR_MODULE, 'test-console', 'test', function(Y) {
        String.prototype.test = function() {
            // simulate Moo monkey-patch
            return false;

        };

        Y.Test.Runner.setName('Dom: Selector');

        var isXHTML = document.createElement("Div").nodeName === "Div";

        Y.Dom = {
            get: function (id) {
                return document.getElementById(id);
            },
            getChildren: function (el) {
                el = typeof el === 'string' ? document.getElementById(el) : el;

                var children = [],
                    i, len;

                for (i = 0, len = el.childNodes.length; i < len; ++i) {
                    if (el.childNodes[i].nodeType === 1) {
                        children.push(el.childNodes[i]);
                    }
                }

                return children;
            },
            getElementsByClassName: function (clz, tag, root) {
                root = typeof root === 'string' ?
                            document.getElementById(root) : root || document;

                var els = root.getElementsByTagName(tag || '*'),
                    nodes = [],
                    S = ' ',
                    i, len;

                clz = S + clz + S;

                for (i = 0, len = els.length; i < len; ++i) {
                    if ((S + els[i].className + S).indexOf(clz)) {
                        nodes.push(els[i]);
                    }
                }

                return nodes;
            },
            getElementsByTagName: function(element, tagName) {
                var result, visited, isUniversal;

                result = [];
                visited = [];
                element = element.firstChild;
                isUniversal = tagName === "*";

                if (!(isUniversal || isXHTML)) {
                    tagName = tagName.toUpperCase();
                }

                while (element) {
                    while (element) {
                        if (element.tagName > "@" && (isUniversal || element.tagName === tagName)) {
                            result.push(element);
                        }

                        visited.push(element);
                        element = element.firstChild;
                    }

                    while (visited.length > 0 && !element) {
                        element = visited.pop().nextSibling;
                    }
                }

                return result;
            },
            getFirstChild: function (el) {
                el = typeof el === 'string' ? document.getElementById(el) : el;

                var node = el.firstChild;
                while (node && node.nodeType !== 1) {
                    node = node.nextSibling;
                }
                return node;
            },
            getLastChild: function (el) {
                el = typeof el === 'string' ? document.getElementById(el) : el;

                var node = el.lastChild;
                while (node && node.nodeType !== 1) {
                    node = node.previousSibling;
                }
                return node;
            },
            addClass: function (els, clz) {
                var S = ' ',
                    i, len;

                if (typeof els === 'string') {
                    els = [ document.getElementById( els ) ];
                } else if (els.nodeType === 1) {
                    els = [ els ];
                } else if (({}).toString.call(els) === '[object Array]') {
                    for (i = els.length - 1; i >= 0; --i) {
                        if (typeof els[i] === 'string') {
                            els[i] = document.getElementById(els[i]);
                            if (!els[i]) {
                                els.splice(i,1);
                            }
                        }
                    }
                } else {
                    els = [];
                }

                clz = S + clz + S;

                for (i = 0, len = els.length; i < len; ++i) {
                    if ((S + els[i].className + S).indexOf(clz)) {
                        els[i].className += clz.replace(/\s+$/,'');
                    }
                }
            }
        };

        new Y.Test.Console({height:'50em'}).render();

        var suite = new Y.Test.Suite("Dom: Selector");
        var Selector = Y.Selector;
        var Assert = Y.Assert;
        var ArrayAssert = Y.ArrayAssert;

        var $ = Selector.query;
        var demo = Y.Dom.get('demo');
        children = Y.Dom.getChildren(demo);
        var demoFirstChild = children[0];
        var demoLastChild = children[children.length - 1];

        var selectorQueryAll = new Y.Test.Case({
            name: 'Query All',

            testFilter: function() {
                var all = Y.Dom.get('test-inputs').getElementsByTagName('input');
                ArrayAssert.itemsAreEqual([all[0], all[1], all[2]], Selector.filter(all, '[type=checkbox]'), '[type=checkbox]');
                ArrayAssert.itemsAreEqual([], Selector.filter(null, '[type=checkbox]'), 'null input');
                //ArrayAssert.itemsAreEqual([document.getElementById('test-inputs')], Selector.filter(['root-test', 'test-inputs'], 'form'), 'form (string inputs)');
                // no longer supporting string input for element
                //ArrayAssert.itemsAreEqual([document.getElementById('test-inputs')], Selector.filter(['root-test', document.getElementById('test-inputs'), document.createTextNode('foo')], 'form'), 'form (mixed inputs)');
            },
            testTest: function() {
                Assert.isTrue(Selector.test(Y.Dom.get('checkbox-unchecked'), '[type=checkbox], button'), '[type=checkbox], button');
                Assert.isTrue(Selector.test(Y.Dom.get('checkbox-unchecked'), 'button, [type=checkbox]'), 'button, [type=checkbox]');
                Assert.isFalse(Selector.test(Y.Dom.get('checkbox-unchecked'), 'foo, button'), 'foo, button');
                Assert.isFalse(Selector.test(null, '#foo'), ';ull input');
                Assert.isFalse(Selector.test(document.createTextNode('foo'), '#foo'), 'textNode input');
                Assert.isTrue(Selector.test(Y.Dom.get('test-lang-en-us'), '[lang|=en]'), '[lang|=en] (lang="en-us")');
                Assert.isTrue(Selector.test(Y.Dom.get('test-lang-en'), '[lang|=en]'), '[lang|=en] (lang="en")');
                Assert.isFalse(Selector.test(Y.Dom.get('test-lang-none'), '[lang|=en]'), '[lang|=en] false pos');
                Assert.isFalse(Selector.test(Y.Dom.get('checkbox-unchecked'), 'for [type=checkbox]'), 'for [type=checkbox] false pos');
                Assert.isTrue(Selector.test(Y.Dom.get('checkbox-unchecked'), 'form [type=checkbox]'), 'form [type=checkbox]');
                Assert.isFalse(Selector.test(Y.Dom.get('checkbox-unchecked'), 'for [type=checkbox]'), 'for [type=checkbox] false pos');

                Assert.isTrue(Selector.test(Y.Dom.get('checkbox-checked'), '[type=checkbox]:checked'), 'type=checkbox:checked');
                Assert.isTrue(Selector.test(Y.Dom.get('radio-checked'), ':checked'), ':checked (radio)');
                Assert.isFalse(Selector.test(Y.Dom.get('radio-unchecked'), ':checked'), ':checked (radio) false pos');
                Assert.isFalse(Selector.test(Y.Dom.get('checkbox-unchecked'), '[type=checkbox]:checked'), 'type=checkbox:checked false pos');
                Assert.isTrue(Selector.test(Y.Dom.get('checkbox-unchecked'), '[type=checkbox]:not(:checked)'), 'type=checkbox:not(:checked)');

                Assert.isTrue(Selector.test(document.getElementsByTagName('dd')[0], 'dd'), 'dd (dd1)');
                Assert.isTrue(Selector.test(document.getElementsByTagName('dd')[1], 'dd'), 'dd (dd2)');

                Assert.isFalse(Selector.test(document.getElementsByTagName('dd')[0], '.test-dd2'), '.test-dd2 (dd1)');
                Assert.isFalse(Selector.test(document.getElementsByTagName('dd')[1], '.test-dd1'), '.test-dd1 (dd2)');

                Assert.isTrue(Selector.test(document.getElementsByTagName('dd')[0], '.test-dd1'), '.test-dd1');
                Assert.isTrue(Selector.test(document.getElementsByTagName('dd')[1], '.test-dd2'), '.test-dd2');

                Assert.isTrue(Selector.test(document.getElementsByTagName('dd')[0], 'dd'), 'dd (dd1)');
                Assert.isTrue(Selector.test(document.getElementsByTagName('dd')[1], 'dd'), 'dd (dd2)');

                // with optional root arg
                var form = Y.DOM.byId('form-root');
                var input = form.getElementsByTagName('input')[0];
                Assert.isTrue(Selector.test(input, 'input', form));
                Assert.isFalse(Selector.test(Y.DOM.byId('foo-bar'), 'input#foo-bar', form));

                var form = Y.DOM.byId('test-inputs');
                Assert.isFalse(Selector.test(input, 'input', form));
                Assert.isFalse(Selector.test(form, 'form', form));
                Assert.isTrue(Selector.test(form, 'form', form.parentNode));
                Assert.isTrue(Selector.test(Y.DOM.byId('foo-bar'), 'input#foo-bar', form));
                Assert.isFalse(Selector.test(Y.DOM.byId('foo-bar'), '#test-inputs input#foo-bar', form));
                Assert.isTrue(Selector.test(Y.DOM.byId('foo-bar'), '#test-inputs input#foo-bar', form.parentNode));
            },

            testRootQuery: function() {
                var all = Y.Dom.get('nth-test').getElementsByTagName('li');

                ArrayAssert.itemsAreEqual(all, $('li', document), 'document');
                ArrayAssert.itemsAreEqual(all, $('#root-test li'), 'document');
                ArrayAssert.itemsAreEqual([], $('#root-tes li', Y.DOM.byId('demo')), 'false id document');

                Assert.areEqual(
                    $('a span, a', Y.DOM.byId('mod1')).length,
                    $('a, a span', Y.DOM.byId('mod1')).length,
                    "$('a, a span') === $('a span, a')");

                var node = document.createElement('div');
                node.innerHTML = '<li><em>foo</em></li>';
                Assert.areEqual(node.getElementsByTagName('em')[0], $('li em', node, true), 'off-dom: li em');
                Assert.isNull($('div li em', node, true), 'off-dom: div li em');

                Assert.areEqual(Y.DOM.byId('test:colon').getElementsByTagName('h2')[0],
                    $('h2', Y.DOM.byId('test:colon'), true),
                    "$('h2', Y.DOM.byId('test:colon'), true)");


                Assert.areEqual(Y.DOM.byId('demo-last-child'), $('.foo #demo-last-child', document.body, true),
                    "$('.foo #demo-last-child', true, document.body)");

                // standard: no element-scoped query
                //ArrayAssert.itemsAreEqual(document.body.getElementsByTagName('p'), $('body p', document.body), "$('body p', document.body)");

                // based on non-standard behavior
                ArrayAssert.itemsAreEqual([], $('body p', document.body), "$('body p', document.body)");
                ArrayAssert.itemsAreEqual([], $('#root-test li', Y.Dom.get('nth-test')), 'id selector w/root false pos');

            },
            /*
            testNthLastChild: function() {
                var all = Y.Dom.get('nth-test').getElementsByTagName('li');
                var odd = Y.Dom.getElementsByClassName('even', 'li', 'nth-test');
                var even = Y.Dom.getElementsByClassName('odd', 'li', 'nth-test');
                var four1 = Y.Dom.getElementsByClassName('last-four-1', 'li', 'nth-test');

                ArrayAssert.itemsAreEqual(odd, $('li:nth-last-child(odd)'), 'odd');
                ArrayAssert.itemsAreEqual(even, $('li:nth-last-child(2n)'), '2n');
                ArrayAssert.itemsAreEqual(even, $('li:nth-last-child(even)'), 'even');
                ArrayAssert.itemsAreEqual(even, $('li:nth-last-child(2n+0)'), '2n+0');
                ArrayAssert.itemsAreEqual(odd, $('li:nth-last-child(2n+1)'), '2n+1');
                ArrayAssert.itemsAreEqual(four1, $('li:nth-last-child(4n+1)'), '4n+1');
            },
            */
            testNthType: function() {
                var all = Y.Dom.get('nth-test').getElementsByTagName('li');
                var odd = Y.Dom.getElementsByClassName('odd', 'li', 'nth-test');
                var even = Y.Dom.getElementsByClassName('even', 'li', 'nth-test');
                var three1 = Y.Dom.getElementsByClassName('three-1', 'li', 'nth-test');
                var four1 = Y.Dom.getElementsByClassName('four-1', 'li', 'nth-test');
                var four2 = Y.Dom.getElementsByClassName('four-2', 'li', 'nth-test');
                var four3 = Y.Dom.getElementsByClassName('four-3', 'li', 'nth-test');
                var four4 = Y.Dom.getElementsByClassName('four-4', 'li', 'nth-test');
                //ArrayAssert.itemsAreEqual(odd, $('li:nth-of-type(odd)'), 'odd');

                all = Y.Dom.get('nth-type-test').getElementsByTagName('div');
                even = Y.Dom.getElementsByClassName('even', 'div', 'nth-type-test');

                //ArrayAssert.itemsAreEqual(all[2], $('#nth-type-test div:nth-of-type(3)'),
                    //"$('#nth-type-test div:nth-of-type(3)')");

                //ArrayAssert.itemsAreEqual(even, $('#nth-type-test div:nth-of-type(even)'),
                    //"$('#nth-type-test div:nth-of-type(even)')");

                //ArrayAssert.itemsAreEqual(even, $('#nth-type-test div:nth-of-type(2n)'),
                    //"$('#nth-type-test div:nth-of-type(2n)')");

            },
            testNthChild: function() {
                var all = Y.Dom.get('nth-test').getElementsByTagName('li');
                var odd = Y.Dom.getElementsByClassName('odd', 'li', 'nth-test');
                var even = Y.Dom.getElementsByClassName('even', 'li', 'nth-test');
                var three1 = Y.Dom.getElementsByClassName('three-1', 'li', 'nth-test');
                var four1 = Y.Dom.getElementsByClassName('four-1', 'li', 'nth-test');
                var four2 = Y.Dom.getElementsByClassName('four-2', 'li', 'nth-test');
                var four3 = Y.Dom.getElementsByClassName('four-3', 'li', 'nth-test');
                var four4 = Y.Dom.getElementsByClassName('four-4', 'li', 'nth-test');

                //ArrayAssert.itemsAreEqual(even[1], $('li:nth-child(2)'), '2');
                //ArrayAssert.itemsAreEqual(even[1], $('li:nth-child(0n+2)'), '0n+2');
                //ArrayAssert.itemsAreEqual(three1, $('li:nth-child(3n+1)'), '3n+1');
                ArrayAssert.itemsAreEqual(all, $('li:nth-child(n+1)'), 'n+1');

                //from http://www.w3.org/TR/css3-selectors/#nth-child-pseudo examples
                /*
                ArrayAssert.itemsAreEqual(odd, $('li:nth-child(2n+1)'), '2n+1');
                ArrayAssert.itemsAreEqual(odd, $('li:nth-child(odd)'), 'odd');
                ArrayAssert.itemsAreEqual(even, $('li:nth-child(2n+0)'), '2n+0');
                ArrayAssert.itemsAreEqual(even, $('li:nth-child(2n)'), '2n');
                ArrayAssert.itemsAreEqual(even, $('li:nth-child(even)'), 'even');
                ArrayAssert.itemsAreEqual(four1, $('li:nth-child(4n+1)'), '4n+1');
                ArrayAssert.itemsAreEqual(four2, $('li:nth-child(4n+2)'), '4n+2');
                ArrayAssert.itemsAreEqual(four3, $('li:nth-child(4n+3)'), '4n+3');
                ArrayAssert.itemsAreEqual(four4, $('li:nth-child(4n+4)'), '4n+4');
                ArrayAssert.itemsAreEqual(even[0], $('li:nth-child(0n+1)'), '0n+1');
                ArrayAssert.itemsAreEqual(even[0], $('li:nth-child(1)'), '1');
                ArrayAssert.itemsAreEqual(all, $('li:nth-child(1n+0)'), '1n+0');
                ArrayAssert.itemsAreEqual(all, $('li:nth-child(n+0)'), 'n+0');
                */

            },

            testQuery: function() {
                ArrayAssert.itemsAreEqual(document.getElementsByTagName('p'), $('p, p'), 'p, p');
                Assert.areEqual(document.getElementsByTagName('p')[0], $('p', null, true), 'p (firstOnly)');
                ArrayAssert.itemsAreEqual([], $('.Foo'), '.Foo');
                ArrayAssert.itemsAreEqual([document.getElementById('root-test')], $('#root-test'), 'id only');
                ArrayAssert.itemsAreEqual([], $('#demo.bar p'), '#demo.bar p');
                ArrayAssert.itemsAreEqual(Y.Dom.get('demo').getElementsByTagName('p'), $('#demo.foo p'), '#demo.foo p');
                ArrayAssert.itemsAreEqual(Y.Dom.get('demo').getElementsByTagName('p'), $('.foo p'), '.foo p');
                ArrayAssert.itemsAreEqual(Y.Dom.get('demo').getElementsByTagName('p'), $('#demo p'), '#demo p');
                ArrayAssert.itemsAreEqual($('p > em'), [Y.Dom.getFirstChild('demo-first-child')], 'p > em');
                //ArrayAssert.itemsAreEqual(Y.Dom.getElementsByClassName('para'), $('[class~=para]'), '[class~=para]');
                //ArrayAssert.itemsAreEqual(Y.Dom.getElementsByClassName('para'), $('[class~="para"]'), '[class~="para"]');
                ArrayAssert.itemsAreEqual(document.body.getElementsByTagName('p'), $('body div p'), 'body div p');
                ArrayAssert.itemsAreEqual([], $('#demo .madeup'), '#demo .madeup');
                //ArrayAssert.itemsAreEqual(Y.Dom.getElementsByClassName('para', 'p'), $('div .para'), 'div .para');
                //ArrayAssert.itemsAreEqual(Y.Dom.getElementsByClassName('first', null, Y.DOM.byId(demo)), $('#demo .first'), '#demo .first');
                //ArrayAssert.itemsAreEqual(document.getElementsByTagName('div'), $('div'), 'div');
                ArrayAssert.itemsAreEqual(document.body.getElementsByTagName('div'), $('body div'), 'body div');
                ArrayAssert.itemsAreEqual([Y.Dom.get('class-bar')], $('.Bar'), '.Bar');
                //ArrayAssert.itemsAreEqual([], $(null), 'null input');
                ArrayAssert.itemsAreEqual([], $('#fake-id-not-in-doc'), 'id false positive');
                Y.Dom.addClass($('li'), 'first-child');
                Assert.areEqual(document.getElementById('label-checkbox-unchecked'), $('label[for=checkbox-unchecked]', null, true), 'for attribute');
                var root = Y.DOM.byId('test-inputs');
                Assert.areEqual($('.not-button', root).length, $('input:not([type=button])', root).length, '.not-button = input:not([type=button]) - root query');
                //ArrayAssert.itemsAreEqual(Y.Dom.get('demo2').getElementsByTagName('div'), $('div:contains(child of demo2)', Y.DOM.byId('demo2')), 'div:contains:(child of demo2) ');
                //Assert.areEqual(document.getElementById('contains-special'), $(':contains(contains "\' & ])'), 'contains "\' & ]');
                Assert.areEqual(Y.DOM.byId('test-select'), $('#test-select', null, true), "($('#test-select'), null, true)");
                Assert.areEqual(document.getElementsByTagName('link')[0], $('[rel^=style]', null, true), "($('[rel^=style]), null, true)");
                Assert.areEqual(Y.DOM.byId('test-rel-div'), $('div[rel^=foo2]', null, true), "($('[rel^=foo2]), null, true)");
                Assert.areEqual(Y.DOM.byId('test-rel-div'), $("div[rel^='foo2']", null, true), "($('[rel^=\'foo2\']), null, true)");
                Assert.areEqual(Y.DOM.byId('foo-bar'), $("input[name='foo.bar']", null, true), "input[name='foo.bar'], null, true)");

                Assert.areEqual(Y.DOM.byId('foo-bar'), $("[id^='foo-']", null, true), "[id^='foo-'], null, true");
                Assert.areEqual(Y.DOM.byId('test-custom-attr'), $("#test-custom-attr[foo=bar]", null, true), "#test-custom-attr[foo=bar], null, true");
                Assert.areEqual(Y.DOM.byId('test-custom-attr'), $("div[foo=bar]", null, true), "div[foo=bar], null, true");
                Assert.areEqual(Y.DOM.byId('test-custom-attr'), $("div#test-custom-attr[foo=bar]", null, true), "div#test-custom-attr[foo=bar], null, true");

                Assert.areEqual(Y.DOM.byId('test-custom-attr'), $("div[foo]", null, true), "div[foo], null, true");
                ArrayAssert.itemsAreEqual([Y.DOM.byId('test-custom-attr')], $("[foo]"), "[foo]");

                Assert.areEqual(document.body, $('body, span', null, true), "$('body, span', null, true)");

                Assert.areEqual(document.getElementById('foo-bar'),
                    $('[id=foo-bar]', null, true),
                    "$('[id=foo-bar]', null, true)");

                Assert.areEqual(document.getElementById('test-custom-attr'),
                    $('[foo-bar]', null, true),
                    "$('[foo-bar]', null, true)");

                Assert.areEqual(document.getElementById('foo-bar'),
                    $('[aria-controls]', null, true),
                    "$('[aria-controls]', null, true)");

                Assert.areEqual(document.getElementById('foo-bar'),
                    $('[-hyphen-start]', null, true),
                    "$('[-hyphen-start]', null, true)");

                Assert.areEqual(Y.DOM.byId('test-lang-en-us'), $('[lang|=en]', null, true), "$('[lang|=en]')");

                Assert.areEqual(Y.DOM.byId('href-test'), $('[href$=".html"]', null, true), "$('[href$=\".html\"]')");
                Assert.isNull($('[href$="?html"]', null, true), "$('[href$=\?html\]')");

                /* failing in brute (ticket 2528057)
                Assert.areEqual(Y.DOM.byId('test:colon'),
                    $('#test\\:colon', null, true),
                    "$('#test\\:colon', null, true)");
                */
            },

            'should find all queries in group': function() {
                Assert.areEqual(14, Y.Selector.query('input[type=foo], input[type=hidden], input[type=checkbox], input[type=radio], input[type=button], input[type=bar]').length);
            }
        });

        var simpleTest = new Y.Test.Case({
            name: 'Simple Node Test',

            testPseudo: function() {
                Assert.isTrue(Selector.test(Y.Dom.getLastChild('demo'), ':last-child'), 'last-child');
                Assert.isTrue(Selector.test(Y.Dom.getFirstChild('demo'), 'p:first-child'), 'first-child tag');
                Assert.isTrue(Selector.test(Y.Dom.getFirstChild('demo'), ':first-child'), 'first-child');
                Assert.isFalse(Selector.test(Y.Dom.getFirstChild('demo'), ':first-child.last'), 'first-child class false pos');
                Assert.isTrue(Selector.test(Y.Dom.getFirstChild('demo'), ':first-child.first'), 'first-child class');
                Assert.isFalse(Selector.test(Y.Dom.getFirstChild('demo'), ':only-child'), 'only-child');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), ':not(p)'), 'not(p)');
                Assert.isTrue(Selector.test(demoFirstChild, ':not(.last)'), 'not(.last)');
                Assert.isTrue(Selector.test(Y.Dom.getFirstChild('demo'), ':first-of-type'), 'first-of-type');
                Assert.isTrue(Selector.test(Y.Dom.getLastChild('demo'), ':last-of-type'), 'last-of-type');
                Assert.isFalse(Selector.test(Y.Dom.getFirstChild('demo'), ':only-of-type'), 'only-of-type false pos');
                Assert.isFalse(Selector.test(Y.Dom.get('demo2'), ':empty'), 'empty false pos');
                Assert.isTrue(Selector.test(Y.Dom.get('empty'), ':empty'), 'empty');
                Assert.isFalse(Selector.test(Y.Dom.get('demo'), ':not(.foo)'), 'not(.foo)');

                /* non-standard
                Assert.isTrue(Selector.test(Y.Dom.get('demo2'), ':contains(demo2)'), 'contains(demo2)');
                Assert.isFalse(Selector.test(Y.Dom.get('demo2'), ':not(:contains(demo2))'), ':not(:contains(demo2))');
                Assert.isTrue(Selector.test(Y.Dom.get('demo2'), ':not(:contains(demo1))'), ':not(:contains(demo1))');
                Assert.isTrue(Selector.test(Y.Dom.get('demo2'), ':contains(child of demo2)'), 'contains(child of demo2)');
                */
            },

            testAttr: function() {
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), '[title]'), 'title exists');
                Assert.isFalse(Selector.test(Y.Dom.getFirstChild('demo'), '[title]'), 'title exists false pos');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), '[id=demo]'), 'id equality');
                Assert.isFalse(Selector.test(Y.Dom.get('demo'), '[id|=me]'), 'id starts with false pos');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), '[id~=demo]'), 'id includes');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), '[title~=demo]'), 'title includes');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), '[id^=de]'), 'id starts with');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), '[id$=mo]'), 'id ends with');
                Assert.isFalse(Selector.test(Y.Dom.get('demo'), '[id$=m]'), 'id ends with false pos');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), '[id*=em]'), 'id substr');
                Assert.isFalse(Selector.test(Y.Dom.get('demo'), '[id*=ex]'), 'id substr false pos');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), '[id=demo][title~=demo]'), 'multiple attributes');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), 'div[id=demo][title~=demo]'), 'tag & multiple attributes');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), 'div[title="this is a demo"]'), 'quoted attribute with spaces');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), "div[title='this is a demo']"), 'single quoted attribute with spaces');
                Assert.isTrue(Selector.test(Y.Dom.get('href-test'), '[href="foo.html"]'), 'href="foo.html"');
                Assert.isTrue(Selector.test(Y.Dom.get('href-test'), '[href$=".html"]'), 'href$=".html"');
                Assert.isFalse(Selector.test(Y.Dom.get('demo'), '[id=demo][title=demo]'), 'multiple attributes false pos');

                /* non-standard
                alert(Y.Dom.get('demo').querySelector('[id!=bar]'));
                Assert.isTrue(Selector.test(Y.Dom.get('href-test'), '[href=foo.html]'), 'href=foo.html');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), 'div[title=this is a demo]'), 'attribute with spaces');
                */
            },

            testClass: function() {
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), '.foo'), 'class match');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), 'div.foo'), 'tag match');
                Assert.isFalse(Selector.test(Y.Dom.get('demo'), 'span.foo'), 'tag false positive');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), '#demo.foo'), 'id match');
                Assert.isFalse(Selector.test(Y.Dom.get('demo'), '.baz'), 'class false positive');
                Assert.isTrue(Selector.test(Y.Dom.getFirstChild('demo'), '.first.para'), 'multiple class match');
                Assert.isTrue(Selector.test(Y.Dom.getFirstChild('demo'), 'p.first.para'), 'tag & multiple class match');
                Assert.isFalse(Selector.test(Y.Dom.get('demo'), '.foo.bar'), 'multiple class false pos');
            },

            testId: function() {
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), '#demo'), 'id match');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), 'div#demo'), 'tag match');
                Assert.isFalse(Selector.test(Y.Dom.get('demo'), 'div#dmo'), 'id false positive');
                Assert.isFalse(Selector.test(Y.Dom.get('demo'), 'span#demo'), 'tag false positive');
            },

            testTag: function() {
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), 'div'), 'div');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), 'body div'), 'body div');
                Assert.isFalse(Selector.test(Y.Dom.get('demo'), 'span'), 'tag false positive');
                Assert.isTrue(Selector.test(Y.Dom.get('demo'), '*'), 'universal tag');
            },

            testDisabled: function() {
                Assert.areEqual(Y.DOM.byId('disabled-button'), $('#test-inputs :disabled', null, true), "$('#test-inputs :disabled')");
            },

            testEnabled: function() {
                Assert.areEqual(Y.DOM.byId('checkbox-unchecked'), $('#test-inputs input:enabled', null, true), "$('#test-inputs input:enabled')");
            },

            testIframe: function() {
                var frameDoc = Y.DOM.byId('test-frame').contentWindow.document;
                Assert.areEqual('iframe foo', Y.Lang.trim($('#demo li', frameDoc, true).innerHTML), "Y.get('#demo li', frameDoc, true)");
            },


            // to support:
            // $('> p', myNode) === ('#my-node-id > p');
            // $('+ p', myNode) === ('#my-node-id + p');
            // $('~ p', myNode) === ('#my-node-id ~ p');
            testScoped: function() {
                var demo = Y.DOM.byId('demo'),
                    paraChildren = [];
                Y.each(demo.getElementsByTagName('p'), function(el) {
                    if (el.parentNode === demo) {
                        paraChildren.push(el);
                    }
                });

                ArrayAssert.itemsAreEqual(paraChildren, $('> p', Y.DOM.byId('demo')), '#demo > p');
                ArrayAssert.itemsAreEqual($('#demo > p'), $('foo, > p', Y.DOM.byId('demo')), '#demo foo, #demo > p');

                //ArrayAssert.itemsAreEqual([Y.Dom.get('demo2')], $('+ div', Y.DOM.byId('demo')), '#demo + div');
                //ArrayAssert.itemsAreEqual($('#demo-first-child ~ p'), $('~ p', Y.DOM.byId('demo-first-child')), '#demo-first-child ~ div');
            },

            'should run scoped query when form has input with name="id"': function() {
                Assert.areEqual(1, Y.Selector.query('label', document.getElementById('test-inputs')).length);
                Assert.areEqual(1, Y.Selector.query('label', document.getElementsByTagName('form')[1]).length);
            },

            'should run scoped query when root has ID with special chars': function() {
                Assert.areEqual(1, Y.Selector.query('span', document.getElementsByTagName('code')[0]).length);
            },

            testAncestor: function() {
                var node = Y.DOM.byId('demo-first-child');
                Assert.isNull(Y.Selector.ancestor(node, 'p'));
                Assert.areEqual(node, Y.Selector.ancestor(node, 'p', true));
                Assert.areEqual(document.body, Y.Selector.ancestor(node, 'body', true));
                Assert.areEqual(node.parentNode, Y.Selector.ancestor(node, 'div'));
            },

            testFragment: function() {
                /*
                    <div>
                        <p></p>
                        <div>
                            <p></p>
                            <div>
                                <p></p>
                            </div>
                            <p></p>
                        </div>
                        <div></div>
                        <p></p>
                        <p></p>
                        <p></p>
                    </div>

                    This is the tree in which the horror below results in.
                */
                var fragment, div, div2, div3;

                fragment = document.createDocumentFragment();

                div = document.createElement("div");
                div.appendChild(document.createElement("p"));

                div2 = document.createElement("div");
                div2.appendChild(document.createElement("p"));

                div3 = document.createElement("div");
                div3.appendChild(document.createElement("p"));

                div2.appendChild(div3);
                div2.appendChild(document.createElement("p"));

                div.appendChild(div2);

                div.appendChild(document.createElement("div"));
                div.appendChild(document.createElement("p"));
                div.appendChild(document.createElement("p"));
                div.appendChild(document.createElement("p"));

                fragment.appendChild(div);

                // NOTE: skipNative is passed in as true.
                // Why? We want to simulate browsers who do not support QSA on document fragments.
                ArrayAssert.itemsAreEqual(Y.Dom.getElementsByTagName(fragment, "*"), $("*", fragment, false, true), "*");
                ArrayAssert.itemsAreEqual(Y.Dom.getElementsByTagName(fragment, "div"), $("div", fragment, false, true), "div");
                ArrayAssert.itemsAreEqual(Y.Dom.getElementsByTagName(fragment, "p"), $("p", fragment, false, true), "p");
            },

            'should return all checked inputs': function() {
                Assert.areEqual(4, Y.Selector.query('#test-inputs :checked', null).length);
            },

            'should apply function for filter test': function() {
                var nodes = document.getElementsByTagName('div'),
                    filtered = Y.Selector.filter(nodes, function(n) {
                        return n.tagName == 'SPAN';
                    });

                ArrayAssert.itemsAreEqual([], filtered);
            },

            'should find element with quoted number ID': function() {
                var node = document.getElementById('1');

                Assert.isNotNull(node);
                Assert.areEqual(node, Y.Selector.query('[id="1"]', null, true));
            },

            'should find element with number ID': function() {
                var node = document.getElementById('1');

                Assert.isNotNull(node);
                Assert.areEqual(node, Y.Selector.query('[id=1]', null, true));
            },

            'should find element with colon in quoted ID': function() {
                var node = document.getElementById('foo:bar');

                Assert.isNotNull(node);
                Assert.areEqual(node, Y.Selector.query('[id="foo:bar"]', null, true));
            },

            'should find element with colon in ID': function() {
                var node = document.getElementById('foo:bar');

                Assert.isNotNull(node);
                Assert.areEqual(node, Y.Selector.query('[id=foo:bar]', null, true));
            },

            'should find element with colon in ID shorthand': function() {
                var node = document.getElementById('foo:bar');

                Assert.isNotNull(node);
                Assert.areEqual(node, Y.Selector.query('#foo\\:bar', null, true));
            },

            'should find element with number ID shorthand': function() {
                var node = document.getElementById('1');

                Assert.isNotNull(node);
                Assert.areEqual(node, Y.Selector.query('#1', null, true));
            },

            'should omit comments from children': function() {
                var node = document.getElementById('test-dl');
                Assert.areEqual(document.getElementById('last-dd'),
                        Y.Selector.query(':last-child', node, true));
            },

            'should allow documentFragment root node': function() {
                var frag = document.createDocumentFragment();
                frag.appendChild(document.createElement('div'));
                Assert.areEqual('DIV',
                        Y.Selector.query('div', frag, true).tagName);

                frag.appendChild(document.createElement('span'));
                Assert.areEqual('SPAN',
                        Y.Selector.query('span', frag, true).tagName);
            },

            'should find custom element by tag': function() {
                var node = Y.Selector.query('foo', null, true);
                Assert.areEqual(document.getElementsByTagName('foo')[0], node);
            },

            'should find custom element by class': function() {
                var node = Y.Selector.query('.custom-foo', null, true);
                Assert.areEqual(document.getElementsByTagName('foo')[0], node);
            },

            'should find custom element by tag and class': function() {
                var node = Y.Selector.query('foo.custom-foo', null, true);
                Assert.areEqual(document.getElementsByTagName('foo')[0], node);
            },

            'should support multiple :not pseudos': function() {
                Assert.areEqual(1, Y.Selector.query('tr:not(.class1):not(.class2)').length);
            },

            'should test multiple :not pseudos with attribute': function() {
                var node = document.getElementById('href-test');
                Assert.isTrue(Selector.test(node, 'a:not(.foo):not([target])'));
            },

            'should support boolean custom attribute via quoted attr': function() {
                var node = document.createElement('div');
                node.setAttribute('foo', true);

                document.body.appendChild(node);
                Assert.areEqual(1, Y.Selector.query('[foo="true"]').length);
                document.body.removeChild(node);
            },

            'should allow special chars in attribute': function() {
                var node = document.getElementById('test-chars');

                Assert.areEqual(node, Y.Selector.query('input[value="ML キshigo, Mfg.,& Co."]', null, true));
            },

            'should allow special chars in attribute, scoped query': function() {
                var form = document.getElementById('test-chars-form'),
                    node = document.getElementById('test-chars');

                Assert.areEqual(node, Y.Selector.query('input[value="ML キshigo, Mfg.,& Co."]', form, true));
            }
        });

        suite.add(selectorQueryAll);
        suite.add(simpleTest);
        Y.Test.Runner.add(suite);
        Y.Test.Runner.run();
    });
};
</script>
<style type="text/css">

</style>
</head>
<body class="yui3-skin-sam">
    <div id="demo" class="foo" title="this is a demo">
        <p class="para first" id="demo-first-child"><em>lorem ipsum</em></p>
        <p class="para">lorem ipsum</p>
        <p class="para last">lorem ipsum</p>
        <div><p>div lorem</p></div>
        <div id="demo-last-child"><p>last child</p></div>
    </div>

    <div id="demo2">
        <div>child of demo2</div>
    </div>

    <div id="empty"></div>

    <select id="test-select" name="test-select">
        <option value="0">foo</option>
        <option value="1">bar</option>
        <option>baz</option>
    </select>
    <div id="root-test">
        <ol id="nth-test">
            <li class="odd three-1 four-1">foo</li>
            <li class="even four-2 last-four-1">foo</li>
            <li class="odd four-3">foo</li>
            <li class="even three-1 four-4">foo</li>
            <li class="odd four-1">foo</li>
            <li class="even four-2 last-four-1">foo</li>
            <li class="odd three-1 four-3">foo</li>
            <li class="even four-4" id="test-lang-none">foo</li>
            <li class="odd four-1" lang="en-US" id="test-lang-en-us">foo</li>
            <li class="even three-1 four-2 last-four-1" lang="en" id="test-lang-en">foo</li>
        </ol>
    </div>
    <div id="nth-type-test">
        <span>span</span>
        <div>first div</div>
        <span>span</span>
        <div class="even">second div</div>
        <div>third div</div>
        <div class="even">fourth div</div>
        <span>span</span>
    </div>
    <a id="href-test" href="foo.html">foo</a>
    <form>
        <input name="id" value="input-named-id">
    </form>
    <form id="test-inputs">
        <label for="checkbox-unchecked" id="label-checkbox-unchecked">label</label>
        <input type="checkbox" id="checkbox-unchecked" class="not-button">
        <input type="checkbox" checked id="checkbox-checked-noval" class="not-button">
        <input type="checkbox" checked="true" id="checkbox-checked" class="not-button">
        <input type="radio" id="radio-unchecked" class="not-button">
        <input type="radio" checked="true" id="radio-checked" class="not-button">
        <input type="button" value="foo" disabled id="disabled-button">
        <input id="foo-bar" name="foo.bar" class="not-button" aria-controls="foo-bar" -hyphen-start="true">
        <input name="id" class="not-button" value="test-input-name">
        <select>
            <option>foo</option>
            <option selected>bar</option>
        </select>
    </form>

    <form id="form-root">
        <input type="button" value="foo">
        <input name="foo" class="text-input">
    </form>

    <div id="mod1">
            <div><h3>H3 - Title</h3></div>
            <div><p>lorem ipsum dolor sit <a href="#" target="_blank"><span>link</span></a></p></div>
    </div>

    <div class="Bar" id="class-bar"></div>
    <div id="contains-special">contains "' & ]</div>
    <div id="test-rel-div" rel="foo2"></div>
    <iframe src="./assets/test-frame.html" id="test-frame"></iframe>

    <dl id="test-dl">
        <dt>Window size</dt>
        <dd class="test-dd1">dd1</dd>
        <dt>Document size</dt>
        <dd id="last-dd" class="test-dd2">dd2</dd>
        <!-- test comment -->
    </dl>
    <div id="test:colon"><h2>test</h2></div>
    <div id="test-custom-attr" foo="bar" foo-bar="bar">custom attribute</div>
    <div id="1"></div>
    <div id="foo:bar"></div>

    <foo class="custom-foo"></foo>
    <table>
        <tbody>
            <tr class="class1"></tr>
            <tr class="class1 class2"></tr>
            <tr></tr>
        </tbody>
    </table>
    <code id="(0:0-178~690#2+21[255], <1>92)">
        <span>foo</span>
    </code>

    <form id="test-chars-form">
        <input id="test-chars" type="hidden" value="ML キshigo, Mfg.,& Co.">
    </form>
</body>
</html>
